#include "task.h"
#include "escal.h"
//#include "http_link_manager.h"
//#include "ftp_link_manager.h"
#include "curl_link_manager.h"
#include "log_manager.h"
#include <QTextStream>
#include <QTextCursor>
#include <QTextDocument>
#include <QTimer>

#define QT_NO_DEBUG

using namespace Escal;

BaseTask::BaseTask(const QUrl &url,const QString &folderName,
	const QString &filename, const int &threadNum, QObject *parent)
	:QObject(parent)
{
	urls << url;
	timer = new QTimer;
	this->threadNum = threadNum;
	this->folderName = folderName;
	this->fileName = filename;
	retry=0;
	fileManager = new FileManager(folderName, fileName);
	status=Stopped;
	textDocument = new QTextDocument(this);
	textCursor = new QTextCursor(textDocument);
	connect(this, SIGNAL(writeLog(QTextCursor*, QString, QString)),
		logManager, 
		SLOT(writeLogDocument(QTextCursor*, QString, QString)));
	connect(fileManager, SIGNAL(fileIsInitialized()),
		this, SLOT(fileIsInitialized()));
	connect(fileManager, SIGNAL(finished()),
		this, SLOT(fileManagerQuited()));
	connect(fileManager, SIGNAL(progressForward()),
		this, SLOT(progressForward()));
	connect(timer, SIGNAL(timeout()),
			this, SLOT(updateSpeed()));
	emit writeLog(textCursor, QTime::currentTime()
		.toString(), tr("folder: %1").arg(folderName));
	emit writeLog(textCursor, QTime::currentTime()
		.toString(), tr("filename: %1").arg(filename));
	emit writeLog(textCursor, QTime::currentTime()
		.toString(), tr("url: %1").arg(urls[0].toString()));
	curSpeed = 0.0;
}

BaseTask::~BaseTask(){
	BaseLinkManager *link;
	foreach(link, links){
		link->deleteLater();
		links.removeAll(link);
	}
	fileManager->deleteLater();
}

QString BaseTask::getStatus() const{
	switch(status){
	case Stopped:
		return tr("Stopped");
	case Running:
		return tr("Running");
	case Paused:
		return tr("Paused");
	case Completed:
		return tr("Completed");
	case Failed:
		return tr("Failed");
	case Canceling:
		return tr("Canceling");
	default:
		return tr("Unkown");
	}
};

qint8 BaseTask::getStatusCode() const{
	return status;
}



float BaseTask::getSpeed() const{
	return curSpeed;
};

void BaseTask::updateSpeed() const{
	TimeSnap snap;
	snap.time = QTime::currentTime();
	snap.finishedSize = fileManager->getFinishedSize();
	snapList.append(snap);
	if(snapList.size()==1){
		curSpeed = 0.0;
	}
	else{
		while(snapList.size()>SNAP_NUM) snapList.removeAt(0);
		TimeSnap oldestSnap = snapList[0];
		int interval;
		interval = oldestSnap.time.msecsTo(snap.time);
		if(!interval) return ;
		curSpeed = (snap.finishedSize - oldestSnap.finishedSize)
			*1000.0 / interval;
	}
}

int BaseTask::getTimeLeft() const{
	if(!curSpeed) return 0;
	return (fileManager->getFileSize() - fileManager->getFinishedSize())
		/ curSpeed;
}

QList<QTextDocument *> BaseTask::getTextDocumentList(){
	QList<QTextDocument *> textDocumentList;
	textDocumentList << textDocument;
	textDocumentList << fileManager->getTextDocument();
	BaseLinkManager *link;
	foreach(link, links){
		textDocumentList << link->getTextDocument();
	}
	return textDocumentList;
}

void BaseTask::load(){
	fileManager->checkDigest();
}

void BaseTask::start(){
	if(status==Running || status==Completed) return;
	emit writeLog(textCursor, QTime::currentTime()
		.toString(), tr("status changed to running..."));
	retry=0;
	status=Running;
	//fileManager->checkDigest();
	fileManager->start();
	BaseLinkManager *link;
	foreach(link, links){
		if(!link->isRunning()){
			link->start();
		}
	}
	if(fileManager->isNew()){
		addLink();
	}
	else{
		adjustRunningThreadNum();
	}
	timer->start(500);
	emit taskDataChanged(this, UN_STATUS);
	emit taskDataChanged(this, UN_RESOURCE);
}

void BaseTask::pause(){
	if(status==Paused || status==Completed || status==Stopped ||
		status==Failed) return;
	emit writeLog(textCursor, QTime::currentTime()
		.toString(), tr("status changed to paused..."));
	timer->stop();
	snapList.clear();
	curSpeed = 0.0;
	status=Paused;
	BaseLinkManager *link;
	foreach(link, links){
		if(link->isRunning()){
			link->stop();
		}
	}
	emit taskDataChanged(this, UN_STATUS);
	emit taskDataChanged(this, UN_RESOURCE);
}

void BaseTask::stop(){
	if(status==Stopped || status==Completed || status==Failed) return;
	emit writeLog(textCursor, QTime::currentTime()
		.toString(), tr("status changed to stopped..."));
	status=Stopped; 
	timer->stop();
	curSpeed = 0.0;
	snapList.clear();
	BaseLinkManager *link;
	foreach(link, links){
		//if(link->isRunning()){
		//link->stop();
		//}
		link->stop();
		links.removeAll(link);
	}
	emit taskDataChanged(this, UN_STATUS);
	emit taskDataChanged(this, UN_RESOURCE);
}

void BaseTask::cancel(){
	qDebug() << "BaseTask::cancel()";
	if(status==Failed||status==Completed) return;
	emit writeLog(textCursor, QTime::currentTime()
		.toString(), tr("status changed to failed..."));
	status=Failed;
	timer->stop();
	snapList.clear();
	curSpeed = 0.0;
	BaseLinkManager *link;
	foreach(link, links){
		link->stop();
		links.removeAll(link);
	}
}

void BaseTask::deleteData(){
	qDebug() << "BaseTask::deleteData()";
	fileManager->remove();
}


void BaseTask::fileManagerQuited(){
	qDebug() << "BaseTask::fileManagerQuited()";
	if(status == Failed) return;
	if(fileManager->hasError()){
		cancel();
	}
	else if(status==Running){
		status=Completed;
		emit writeLog(textCursor, QTime::currentTime()
			.toString(), tr("status changed to completed..."));
	}
	qDebug() << "BaseTask::fileManagerQuited()";
	emit taskDataChanged(this, UN_STATUS);
	emit taskDataChanged(this, UN_RESOURCE);
}

void BaseTask::linkManagerQuited(BaseLinkManager *link, bool error){
	qDebug() << "BaseTask::linkManagerQuited" << link;
	if(status!=Paused){
		//delete link;
		//qDebug() << "HttpTask::linkManagerQuited 5";
		link->deleteLater();
		//qDebug() << link;
		//delete link;
		//link->cleanUp();
		//qDebug() << "HttpTask::linkManagerQuited 4";
		links.removeAll(link);
	}
	if(status==Running){
		retry += error;
		if(retry >= 30){
			cancel();
		}else{
			adjustRunningThreadNum();
		}
	}
	emit taskDataChanged(this, UN_STATUS);
	emit taskDataChanged(this, UN_RESOURCE);
}

void BaseTask::adjustRunningThreadNum(){
	qDebug() << "BaseTask::adjustRunningThreadNum";
	if(retry >30) cancel();
	BaseLinkManager *link;
	int num = this->threadNum;
	if(num > fileManager->getUndonePieceNum()){
		num = fileManager->getUndonePieceNum();
	}
	if(!fileManager->isAcceptRange()){
		printf("!fileManager->isAcceptRange()\n");
		num = 1;
		printf("force thread number to : %d\n", num);

	}
	if(links.size() < num){
		int i = num - links.size();
		while(i-->0){
			addLink();
		}
	}
	else {
		while(links.size() > num){
			link = links[links.size() - 1];
			//link->deleteLater();
			link->stop();
			links.removeAll(link);
		}
	}
	emit taskDataChanged(this, UN_RESOURCE);
}

void BaseTask::fileIsInitialized(){
	emit taskDataChanged(this, UN_LENGTH);
	if(status==Running){
		adjustRunningThreadNum();
	}

}

void BaseTask::progressForward(){
	emit taskDataChanged(this, UN_REMAIN);
	emit taskDataChanged(this, UN_PROGRESS);
}

void BaseTask::increase(){
	threadNum++;
	if(threadNum>MAX_THREAD_NUM)
		threadNum = MAX_THREAD_NUM;
	if(status==Running){
		adjustRunningThreadNum();
	}
	emit taskDataChanged(this, UN_RESOURCE);
}

void BaseTask::cutDown(){
	threadNum--;
	if(threadNum<1)
		threadNum = 1;
	if(status==Running){
		adjustRunningThreadNum();
	}
	emit taskDataChanged(this, UN_RESOURCE);
}

// ******************************** HttpTask *******************************
/*
HttpTask::HttpTask(const QUrl &url,const QString &folderName, const QString 
		&filename, const int &threadNum, QObject *parent)
	:BaseTask(url, folderName, filename, threadNum, parent)
{

}

void HttpTask::addLink(){
	qDebug() << "HttpTask::addLink()";
	BaseLinkManager *link = new HttpLinkManager(fileManager, urls[0]);
	links << link;
	// connect( link, SIGNAL(linkQuited(HttpLinkManager *, bool)),
	//	this, SLOT(linkQuited(HttpLinkManager *, bool)));
	connect(link, SIGNAL(linkQuited(BaseLinkManager *, bool )), 
		this, SLOT(linkManagerQuited(BaseLinkManager *, bool)));
	link->start();
}
*/
// ******************************** FtpTask ********************************
/*
FtpTask::FtpTask(const QUrl &url,const QString &folderName, const QString 
		&filename, const int &threadNum, QObject *parent)
	:BaseTask(url, folderName, filename, threadNum, parent)
{
	this->threadNum = 1;
}

void FtpTask::addLink(){
	BaseLinkManager *link = new FtpLinkManager(fileManager, urls[0]);
	links << link;
	// connect( link, SIGNAL(linkQuited(HttpLinkManager *, bool)),
	//	this, SLOT(linkQuited(HttpLinkManager *, bool)));
	connect(link, SIGNAL(linkQuited(BaseLinkManager *, bool )), 
		this, SLOT(linkManagerQuited(BaseLinkManager *, bool)));
	link->start();
}
*/
// ************************** CurlTask **********************************

CurlTask::CurlTask(const QUrl &url,const QString &folderName, const QString 
		&filename, const int &threadNum, QObject *parent)
	:BaseTask(url, folderName, filename, threadNum, parent)
{
	//this->threadNum = 1;
}

void CurlTask::addLink(){
	BaseLinkManager *link = new CurlLinkManager(fileManager, urls[0]);
	links << link;
	// connect( link, SIGNAL(linkQuited(HttpLinkManager *, bool)),
	//	this, SLOT(linkQuited(HttpLinkManager *, bool)));
	connect(link, SIGNAL(linkQuited(BaseLinkManager *, bool )), 
		this, SLOT(linkManagerQuited(BaseLinkManager *, bool)));
	link->start();
}



// *************************** TaskFactory *******************************

BaseTask *TaskFactory::createNewTask(const QUrl &url,const QString &folder,
	const QString &filename, const int &thread, QObject *parent)
{
	return new CurlTask(url, folder, filename, thread, parent);
	/* if(url.scheme() == "http" || url.scheme() == "https"){
		return new HttpTask(url, folder, filename, thread, parent);
	}
	else if(url.scheme() == "ftp"){
		return new FtpTask(url, folder, filename, thread, parent);
	}
	else{
		return NULL;
	}
	*/
}



